package com.tjc.ratelimit;

import java.util.Arrays;

/**
 * @Author: tjc
 * @description
 * @Date Created in 0:00 2022-02-21
 * 最小粗粒度为1ms
 */
class SlidingWindowPolicy implements RateLimitPolicy {

    int limit;
    int capacity;
    int[] rateBuckets;
    long startMs;
    long currentTime;
    long tickMs;


    public SlidingWindowPolicy(int limit, int size, long tickMs) {
        this.limit = limit;
        this.capacity = size + 1;
        long curMs = System.currentTimeMillis();
        this.startMs = curMs - (curMs % tickMs);
        this.tickMs = tickMs;
        this.currentTime = this.startMs - (this.startMs % tickMs);
        this.rateBuckets = new int[capacity];
    }

    @Override
    public synchronized boolean access(String serviceName) {
        long curMs = System.currentTimeMillis();
        advanceClock(curMs);
        long virtualId = currentTime / tickMs;
        int index = (int) (virtualId % capacity);
        clearExpiredRate(index);
        int rateCount = getTotalRate(index);
        if (rateCount >= limit) {
            return false;
        } else {
            rateBuckets[index] += 1;
            return true;
        }
    }

    private void clearExpiredRate(int index) {
        rateBuckets[(index + 1) % capacity] = 0;
    }

    private int getTotalRate(int index) {
        clearExpiredRate(index);
        return Arrays.stream(rateBuckets).sum();
    }

    private void advanceClock(long timeMs) {
        if (timeMs >= currentTime + tickMs) {
            currentTime = timeMs - (timeMs % tickMs);
        }
    }
}
